package com.yugao.fintech.antelope.pay.payment.controller.refund;

import com.yugao.fintech.antelope.base.security.annotation.Anonymous;
import com.yugao.fintech.antelope.pay.common.constants.PayNodeEnum;
import com.yugao.fintech.antelope.pay.common.service.PayNodeService;
import com.yugao.fintech.antelope.pay.common.service.RefundOrderService;
import com.yugao.fintech.antelope.pay.model.entity.PayNode;
import com.yugao.fintech.antelope.pay.model.entity.RefundOrder;
import com.yugao.fintech.antelope.pay.payment.channel.AbstractChannelRefundNoticeService;
import com.yugao.fintech.antelope.pay.payment.channel.IChannelRefundNoticeService;
import com.yugao.fintech.antelope.pay.payment.model.bo.MchAppConfigContext;
import com.yugao.fintech.antelope.pay.payment.model.rqrs.ChannelRetMsgExt;
import com.yugao.fintech.antelope.pay.payment.process.ChannelRefundOrderProcess;
import com.yugao.fintech.antelope.pay.payment.service.ConfigContextQueryService;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

/**
 * 渠道侧的退款通知入口Controller 【异步回调(doNotify) 】
 */
@Slf4j
@Controller
@Anonymous
public class ChannelRefundNoticeController {

    @Autowired
    private RefundOrderService refundOrderService;
    @Autowired
    private ConfigContextQueryService configContextQueryService;
    @Autowired
    private ChannelRefundOrderProcess channelRefundOrderProcess;
    @Autowired
    private PayNodeService payNodeService;

    /**
     * 异步回调入口
     */
    @ResponseBody
    @RequestMapping(value = {"/refund/notify/{ifCode}", "/refund/notify/{ifCode}/{refundOrderId}"})
    public ResponseEntity<?> doNotify(HttpServletRequest request, @PathVariable("ifCode") String ifCode, @PathVariable(value = "refundOrderId", required = false) String urlOrderNo) {

        String refundOrderNo = null;
        String logPrefix = "进入[" + ifCode + "]退款回调：urlOrderNo：[" + StringUtils.defaultIfEmpty(urlOrderNo, "") + "] ";
        log.info("===== {} =====", logPrefix);
        PayNode payNode = PayNode.builder().titleCode(PayNodeEnum.REFUND_CALLBACK_OK.getCode()).build();
        try {

            // 参数有误
            if (StringUtils.isEmpty(ifCode)) {
                return ResponseEntity.badRequest().body("ifCode is empty");
            }

            // 查询退款接口是否存在
            IChannelRefundNoticeService refundNotifyService = AbstractChannelRefundNoticeService.getService(ifCode);

            // 支付通道接口实现不存在
            if (refundNotifyService == null) {
                log.error("{}, interface not exists ", logPrefix);
                return ResponseEntity.badRequest().body("[" + ifCode + "] interface not exists");
            }

            // 解析订单号 和 请求参数
            MutablePair<String, Object> mutablePair = refundNotifyService.parseParams(request, urlOrderNo, IChannelRefundNoticeService.NoticeTypeEnum.DO_NOTIFY);
            if (mutablePair == null) { // 解析数据失败， 响应已处理
                log.error("{}, mutablePair is null ", logPrefix);
                throw new BizException("解析数据异常！"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }

            // 解析到订单号
            refundOrderNo = mutablePair.left;
            log.info("{}, 解析数据为：refundOrderId:{}, params:{}", logPrefix, refundOrderNo, mutablePair.getRight());

            if (StringUtils.isNotEmpty(urlOrderNo) && !urlOrderNo.equals(refundOrderNo)) {
                log.error("{}, 订单号不匹配. urlOrderNo={}, refundOrderId={} ", logPrefix, urlOrderNo, refundOrderNo);
                throw new BizException("退款单号不匹配！");
            }

            //获取订单号 和 订单数据
            RefundOrder refundOrder = refundOrderService.getById(refundOrderNo);

            // 订单不存在
            if (refundOrder == null) {
                log.error("{}, 退款订单不存在", logPrefix);
                return refundNotifyService.doNotifyOrderNotExists(request);
            }
            payNode.setPayOrderNo(refundOrder.getPayOrderNo());

            // 查询出商户应用的配置信息
            MchAppConfigContext mchAppConfigContext = configContextQueryService.queryMchInfoAndAppInfo(refundOrder.getMchNo(), refundOrder.getAppId());

            // 调起接口的回调判断
            ChannelRetMsgExt notifyResult = refundNotifyService.doNotice(request, mutablePair.getRight(), refundOrder, mchAppConfigContext, IChannelRefundNoticeService.NoticeTypeEnum.DO_NOTIFY);

            // 返回null 表明出现异常， 无需处理通知下游等操作。
            if (notifyResult == null || notifyResult.getChannelState() == null || notifyResult.getResponseEntity() == null) {
                log.error("{}, 处理回调事件异常  notifyResult data error, notifyResult ={} ", logPrefix, notifyResult);
                throw new BizException("处理回调事件异常！"); //需要实现类自行抛出ResponseException, 不应该在这抛此异常。
            }
            // 处理退款订单
            boolean updateOrderSuccess = channelRefundOrderProcess.handleRefundOrder4Channel(notifyResult, refundOrder);

            // 更新退款订单 异常
            if (!updateOrderSuccess) {
                log.error("{}, updateOrderSuccess = {} ", logPrefix, false);
                payNode.setTitleCode(PayNodeEnum.REFUND_CALLBACK_FAIL.getCode());
                return refundNotifyService.doNotifyOrderStatusUpdateFail(request);
            }

            log.info("===== {}, 订单通知完成。 refundOrderId={}, parseState = {} =====", logPrefix, refundOrderNo, notifyResult.getChannelState());

            return notifyResult.getResponseEntity();

        } catch (Exception e) {
            log.error("{}, refundOrderId={}, exception", logPrefix, refundOrderNo, e);
            payNode.setTitleCode(PayNodeEnum.REFUND_CALLBACK_FAIL.getCode());
            return ResponseEntity.badRequest().body(e.getMessage());
        } finally {
            payNodeService.savePayNode(payNode);
        }
    }
}
